﻿//---------------------------------------------------------------------
// Author: Reinhard Lehrbaum
//
// Description: Search for objects in the Active Directory.
//
// Creation Date: 2006-12-23
//---------------------------------------------------------------------

using System;
using System.Collections;
using System.ComponentModel;
using System.DirectoryServices;
using System.Management.Automation;
using System.Text.RegularExpressions;

namespace Pscx.Commands.DirectoryServices
{
    [Cmdlet(VerbsCommon.Get, "ADObject"),
     Description("Search for objects in the Active Directory/Global Catalog.")]
    public class GetADObjectCommand : Pscx.Commands.Internal.PscxDirectoryServiceCommandBase
    {
        public enum ObjectClass
        {
            User,
            Group,
            Contact,
            Computer,
            OU,
            Volume,
            Printer,
            msMQRecipient
        }


        private string FDomain;
        private ObjectClass[] FClass;
        private string FValue;
        private bool FUseGlobalCatalog;
        private SearchScope FScope = SearchScope.Subtree;
        private string FDN;
        private string FFilter;


        private const string cClassDefault = "(|(name={0})(mail={0})(displayName={0})(displayName={0})(givenName={0})(sAMAccountName={0})(sn={0}))";
        private const string cClassUser = "(&(objectCategory=person)(objectClass=user)(|(cn={0})(displayName={0})(givenName={0})(mail={0})(sn={0})))";
        private const string cClassGroup = "(&(objectClass=group)(|(name={0})(mail={0})))";
        private const string cClassContact = "(&(objectClass=contact)(|(name={0})(mail={0})))";
        private const string cClassComputer = "(&(objectClass=computer)(name={0}))";
        private const string cClassOU = "(&(objectClass=organizationalUnit)(name={0}))";
        private const string cClassVolume = "(&(objectClass=volume)(name={0}))";
        private const string cClassPrinter = "(&(objectClass=printQueue)(name={0}))";
        private const string cClassMSMQRecipient = "(&(objectClass=msMQ-Custom-Recipient)(name={0}))";


        [Parameter(HelpMessage = "Specify the domain name for the search. (Format: canonical name  e.g. some.domain.xx)"),
         DefaultValue("Logon domain from the user.")]
        public string Domain
        {
            get { return FDomain; }
            set { FDomain = value; }
        }

        [Parameter(HelpMessage = "Result returns only objects form the selected classes.")]
        public ObjectClass[] Class
        {
            get { return FClass; }
            set { FClass = value; }
        }

        [Parameter(HelpMessage = "Search string. Wildcards are permitted."),
         AcceptsWildcards(true)]
        public string Value
        {
            get { return FValue; }
            set { FValue = value; }
        }

        [Alias("GC")]
        [Parameter(HelpMessage = "Use Global Catalog for the search"),
         DefaultValue(false)]
        public SwitchParameter GlobalCatalog
        {
            get { return FUseGlobalCatalog; }
            set { FUseGlobalCatalog = value; }
        }

        [Parameter(HelpMessage = "Search scope")]
        public SearchScope Scope
        {
            get { return FScope; }
            set { FScope = value; }
        }

        [Alias("DN")]
        [Parameter(HelpMessage = "Specify the search path (Format: distinguished name  e.g. \"DC=some,DC=domain,DC=xx\")")]
        public string DistinguishedName
        {
            get { return FDN; }
            set { FDN = value; }
        }

        [Parameter(HelpMessage = "Specify the search filter")]
        public string Filter
        {
            get { return FFilter; }
            set { FFilter = value; }
        }
        
 
        protected override void ProcessRecord()
        {
            string dnRoot = "";
            if (FDN == null)
            {
                dnRoot = (FUseGlobalCatalog ? "GC://" : "LDAP://") + (FServer != null ? FServer + "/" : "");
                if (FDomain != null)
                {
                    dnRoot += ConvertToDistinguishedName(FDomain);
                }
                else
                {
                    if (FServer == null)
                    {
                        dnRoot = "";
                    }
                }
                if (dnRoot.Length > 0 && dnRoot.EndsWith("/"))
                {
                    dnRoot = dnRoot.Remove(dnRoot.Length - 1);
                }
            }
            else
            {
                dnRoot = FDN;
                int p = dnRoot.IndexOf("://");
                if (p < 0)
                {
                    dnRoot = (FUseGlobalCatalog ? "GC://" : "LDAP://") + dnRoot;
                }
                else
                {
                    dnRoot = dnRoot.Substring(0, p).ToUpper() + dnRoot.Substring(p);
                }
            }

            DirectoryEntry SearchRoot = GetDirectoryEntry(dnRoot);
            DirectorySearcher Searcher = new DirectorySearcher(SearchRoot);

            Searcher.SearchScope = FScope;

            string SearchFilter = getFilter();
            if (SearchFilter != "")
            {
                Searcher.Filter = SearchFilter;
            }

            foreach (SearchResult result in Searcher.FindAll())
            {
                try
                {
                    WriteObject(GetDirectoryEntry(result.Path));
                }
                catch (Exception ex)
                {
                    WriteError(new ErrorRecord(ex, "", ErrorCategory.InvalidData, result.Path));
                }
            }
        }

        private string ConvertToDistinguishedName(string value)
        {
            string result = "";

            string[] part = value.Split('/');
            if (part.Length > 0)
            {
                result = "DC=" + Regex.Replace(part[0], @"\.", ",DC=");

                for (int i = 1; i < part.Length; i++)
                {
                    result = "OU=" + part[i] + "," + result;
                }
            }

            return result;
        }

        private string getFilter()
        {
            string result = "";

            if (FFilter == null)
            {
                string SearchValue = (FValue != null ? FValue : "*");

                if (FClass != null && FClass.Length > 0)
                {
                    foreach (ObjectClass item in FClass)
                    {
                        result += string.Format(getFilterClass(item), SearchValue);
                    }

                    if (FClass.Length > 1)
                    {
                        result = "(|" + result + ")";
                    }
                }
                else
                {
                    if (!SearchValue.Equals("*"))
                    {
                        result = string.Format(cClassDefault, SearchValue);
                    }
                    else
                    {
                        result = "";
                    }
                }
            }
            else
            {
                result = FFilter;
            }

            return result;
        }

        private string getFilterClass(ObjectClass value)
        {
            switch (value)
            {
                case ObjectClass.User:
                    return cClassUser;
                case ObjectClass.Group:
                    return cClassGroup;
                case ObjectClass.Contact:
                    return cClassContact;
                case ObjectClass.Computer:
                    return cClassComputer;
                case ObjectClass.OU:
                    return cClassOU;
                case ObjectClass.Volume:
                    return cClassVolume;
                case ObjectClass.Printer:
                    return cClassPrinter;
                case ObjectClass.msMQRecipient:
                    return cClassMSMQRecipient;
                default:
                    return "";
            }
        }
   }
}
